package com.example.demo.interceptor;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import com.alibaba.fastjson.JSON;
import com.example.demo.annotation.AccessLimit;
import com.example.demo.model.Result;
import com.jiangwz.utils.IPUtils;
import com.jiangwz.utils.RedisUtil;

/**
 * @author jiangwz
 * @create 2019/10/27.
 */
@Component
public class AccessLimitInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisUtil redisUtil;

    @Value("${server.port}")
    private int port;

    private ReentrantLock reentrantLock = new ReentrantLock();


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
            if (accessLimit == null) {
                return true;
            }
            int limit = accessLimit.limit();
            int second = accessLimit.second();

            // 不加锁有并发问题,加锁后正常,但是可能还是有并发问题,这里应该加分布式锁
            reentrantLock.lock();
            String lockKey = "lockKey:" + request.getRequestURI();
//            boolean lock = RedissonUtils.tryLock(lockKey, TimeUnit.SECONDS, 8, 8);
//            if (lock){
//                RLock rLock = RedissonUtils.lock(lockKey);
                try {
                    String key = "limit:"+ port + ":" +IPUtils.getIpAddr(request) + request.getRequestURI();
                    Integer accessCount = redisUtil.getInteger(key);
                    if (accessCount == null) {
                        redisUtil.set(key, 1);
                        redisUtil.expire(key, second, TimeUnit.SECONDS);
                    }else if (accessCount < limit) {
                        redisUtil.set(key, accessCount + 1);
                        redisUtil.expire(key, second, TimeUnit.SECONDS);
                    }else {
                        output(response, JSON.toJSONString(Result.error("请求太频繁,请稍后尝试.")));
                        return false;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
//                    RedissonUtils.unlock(rLock);
                    reentrantLock.unlock();
                }
            }

//        }
        return true;
    }

    private void output(HttpServletResponse response, String msg) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        ServletOutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            outputStream.write(msg.getBytes("UTF-8"));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            outputStream.flush();
            outputStream.close();
        }
    }
}
